package git.soulbgm.job;

import com.baomidou.mybatisplus.core.toolkit.Sequence;
import git.soulbgm.cache.enumeration.TimeLevel;
import git.soulbgm.cache.handle.HandleGroupData;
import git.soulbgm.common.function.DataHandleFunction;
import git.soulbgm.mapper.StatisticsMapper;
import git.soulbgm.pojo.StatisticsInfo;
import git.soulbgm.utils.DateUtil;
import git.soulbgm.utils.LogHelper;
import git.soulbgm.utils.StringUtil;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static git.soulbgm.common.Constants.*;

/**
 * 统计通用工作
 *
 * @author SoulBGM
 * @date 2023-04-02
 */
@Service
public class StatisticsCommonJob {

    private static final LogHelper LOG = new LogHelper(StatisticsCommonJob.class);

    private final StatisticsMapper mapper;
    private final HandleGroupData handleGroupCacheData;

    public StatisticsCommonJob(StatisticsMapper mapper, HandleGroupData handleGroupCacheData) {
        this.mapper = mapper;
        this.handleGroupCacheData = handleGroupCacheData;
    }

    /**
     * 分组
     *
     * @param groupName           分组名称
     * @param info                统计信息
     * @param startTimeAndEndTime 开始时间和结束时间
     */
    public <T> void group(String groupName, StatisticsInfo info, long[] startTimeAndEndTime) {
        group(groupName, info, startTimeAndEndTime, null);
    }

    /**
     * 分组
     *
     * @param groupName           分组名称
     * @param info                统计信息
     * @param startTimeAndEndTime 开始时间和结束时间
     * @param function            函数
     */
    public <T> void group(String groupName, StatisticsInfo info, long[] startTimeAndEndTime, DataHandleFunction<T> function) {
        int result = statisticsData(groupName, info, startTimeAndEndTime, function);
        if (info.getGroupByAndAllValue() != null && result > 0) {
            performAggregationAndPrintLog(info, startTimeAndEndTime, function);
        }
        List<TimeLevel> timeLevels = info.getTimeLevels();
        int size = timeLevels.size();
        if (size > 1) {
            for (int i = 1; i < size; i++) {
                TimeLevel timeLevel = timeLevels.get(i);
                long[] newStartTimeAndEndTime = getStatisticsTime(startTimeAndEndTime, timeLevel);
                if (newStartTimeAndEndTime.length == 2) {
                    performGroupAndPrintLog(info, newStartTimeAndEndTime, timeLevel, function);
                }
            }
        }
    }

    /**
     * 统计数据
     *
     * @param groupName           组名
     * @param info                统计信息
     * @param startTimeAndEndTime 开始时间和结束时间
     */
    private <T> int statisticsData(String groupName, StatisticsInfo info, long[] startTimeAndEndTime, DataHandleFunction<T> function) {
        try {
            TimeLevel timeLevel = info.getTimeLevels().get(0);
            long groupStartTime = System.currentTimeMillis();
            String[] groupFieldNames = info.getAggregationFields().keySet().toArray(new String[]{});
            List<Map<String, Object>> list = handleGroupCacheData.groupCacheData(timeLevel, groupName, groupFieldNames, info.getGroupBy().toArray(new String[]{}), startTimeAndEndTime[0], startTimeAndEndTime[1], (Class<Map<String, Object>>) (Class<?>) Map.class);
            int result = 0;
            if (!list.isEmpty()) {
                if (function != null && function.isStatisticsHandle()) {
                    function.statisticsAfterHandle(function.convert(list));
                }
                result = mapper.insertStatistics(info.getTablePrefix() + timeLevel.e, getInsertColumns(info), getInsertValues(info, list, startTimeAndEndTime, false));
            }
            String startTime = DateUtil.format(startTimeAndEndTime[0]);
            String endTime = DateUtil.format(startTimeAndEndTime[1]);
            if (result > 0) {
                String targetTable = info.getTablePrefix() + timeLevel.e;
                long groupEndTime = System.currentTimeMillis();
                LOG.info("统计 {} —— {} 的 {} 分组数据到 {} 表成功, 聚合总计耗时 {} 毫秒, 入库 {} 条记录",
                        startTime, endTime, groupName, targetTable, (groupEndTime - groupStartTime), result);
                handleGroupCacheData.deleteGroupCacheData(timeLevel, groupName, startTimeAndEndTime[0], startTimeAndEndTime[1], groupFieldNames.length);
            } else {
                LOG.info("统计 {} —— {} 的 {} 分组数据为空", startTime, endTime, groupName);
            }
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行统计数据聚合并打印日志
     *
     * @param info                统计信息
     * @param startTimeAndEndTime 开始时间和结束时间
     * @param function            函数
     */
    private <T> void performAggregationAndPrintLog(StatisticsInfo info, long[] startTimeAndEndTime, DataHandleFunction<T> function) {
        TimeLevel timeLevel = info.getTimeLevels().get(0);
        long groupStartTime = System.currentTimeMillis();
        int result = statisticsDataAggregation(info, startTimeAndEndTime, timeLevel, function);
        long groupEndTime = System.currentTimeMillis();
        String sourceTable = info.getTablePrefix() + timeLevel.e;
        LOG.info("统计数据聚合 {} —— {} 的 {} 表数据到 {} 表成功, 聚合总计耗时 {} 毫秒, 入库 {} 条记录",
                DateUtil.format(startTimeAndEndTime[0]), DateUtil.format(startTimeAndEndTime[1]),
                sourceTable, sourceTable, (groupEndTime - groupStartTime), result);
    }

    /**
     * 统计数据聚合
     *
     * @param info                统计信息
     * @param startTimeAndEndTime 开始时间和结束时间
     * @param timeLevel           时间等级
     * @param function            函数
     * @return int
     */
    public <T> int statisticsDataAggregation(StatisticsInfo info, long[] startTimeAndEndTime, TimeLevel timeLevel, DataHandleFunction<T> function) {
        List<List<String>> allCombinations = new ArrayList<>();
        generateCombinations(new ArrayList<>(info.getGroupBy()), 0, new ArrayList<>(), allCombinations);

        Map<String, String> map = info.getAggregationFields();
        String aggregation = map.keySet().stream().map(o -> map.get(o) + " as " + o).collect(Collectors.joining(","));

        List<Map<String, Object>> list = new ArrayList<>();
        for (List<String> groupBy : allCombinations) {
            List<Map<String, Object>> mapList = mapper.group(getSelectColumns(groupBy, aggregation), info.getTablePrefix() + timeLevel.e, info.getStatisticsEndTimeField(), startTimeAndEndTime[0], startTimeAndEndTime[1], String.join(",", groupBy));
            if (mapList != null && !mapList.isEmpty()) {
                list.addAll(mapList.stream().filter(Objects::nonNull).collect(Collectors.toList()));
            }
        }

        if (list.isEmpty()) {
            return 0;
        }

        if (function != null && function.isAggregationHandle()) {
            function.aggregationAfterHandle(function.convert(list));
        }

        return mapper.insertStatistics(info.getTablePrefix() + timeLevel.e, getInsertColumns(info), getInsertValues(info, list, startTimeAndEndTime, true));
    }

    /**
     * 执行分组并打印日志
     *
     * @param info                统计信息
     * @param startTimeAndEndTime 开始时间和结束时间
     * @param timeLevel           时间等级
     * @param function            函数
     */
    private <T> void performGroupAndPrintLog(StatisticsInfo info, long[] startTimeAndEndTime, TimeLevel timeLevel, DataHandleFunction<T> function) {
        long groupStartTime = System.currentTimeMillis();
        int result = group(info, startTimeAndEndTime, timeLevel, function);
        long groupEndTime = System.currentTimeMillis();
        String sourceTable = info.getTablePrefix() + timeLevel.getBeforeTimeLevel().e;
        String targetTable = info.getTablePrefix() + timeLevel.e;
        LOG.info("聚合 {} —— {} 的 {} 表数据到 {} 表成功, 聚合总计耗时 {} 毫秒, 入库 {} 条记录",
                DateUtil.format(startTimeAndEndTime[0]), DateUtil.format(startTimeAndEndTime[1]),
                sourceTable, targetTable, (groupEndTime - groupStartTime), result);
    }

    /**
     * 分组
     *
     * @param info                统计信息
     * @param startTimeAndEndTime 开始时间和结束时间
     * @param timeLevel           时间等级
     * @param function            函数
     * @return int
     */
    public <T> int group(StatisticsInfo info, long[] startTimeAndEndTime, TimeLevel timeLevel, DataHandleFunction<T> function) {
        List<Map<String, Object>> list = mapper.group(getSelectColumns(info), info.getTablePrefix() + timeLevel.getBeforeTimeLevel().e, info.getStatisticsEndTimeField(), startTimeAndEndTime[0], startTimeAndEndTime[1], String.join(",", info.getGroupBy()));

        if (list.isEmpty()) {
            return 0;
        }

        if (function != null && function.isGroupHandle()) {
            function.groupAfterHandel(function.convert(list));
        }

        return mapper.insertStatistics(info.getTablePrefix() + timeLevel.e, getInsertColumns(info), getInsertValues(info, list, startTimeAndEndTime, false));
    }

    /**
     * 获取Select列
     *
     * @param info 统计信息
     * @return {@link String}
     */
    public static String getSelectColumns(StatisticsInfo info) {
        return getSelectColumns(info.getGroupBy(), info.getAggregationFields());
    }

    /**
     * 获取Select列
     *
     * @param groupBy           分组字段
     * @param aggregationFields 聚合字段Map
     * @return {@link String}
     */
    public static String getSelectColumns(Collection<String> groupBy, Map<String, String> aggregationFields) {
        return getSelectColumns(groupBy, aggregationFields.keySet().stream().map(o -> aggregationFields.get(o) + " as " + o).collect(Collectors.joining(",")));
    }

    /**
     * 获取Select列
     *
     * @param groupBy     分组字段
     * @param aggregation 聚合字段
     * @return {@link String}
     */
    public static String getSelectColumns(Collection<String> groupBy, String aggregation) {
        StringBuilder columns = new StringBuilder(String.join(",", groupBy));
        if (columns.length() > 0) {
            columns.append(",");
        }
        columns.append(aggregation);
        return columns.toString();
    }

    /**
     * 获取Insert列
     *
     * @param info 统计信息
     * @return {@link String}
     */
    public static String getInsertColumns(StatisticsInfo info) {
        StringBuilder columns = new StringBuilder(info.getIdField()).append(",");
        columns.append(String.join(",", info.getGroupBy())).append(",");
        columns.append(String.join(",", info.getAggregationFields().keySet()));
        columns.append(",").append(info.getStatisticsStartTimeField());
        columns.append(",").append(info.getStatisticsEndTimeField());
        return columns.toString();
    }

    private static final Sequence SEQUENCE = new Sequence(1, 1);

    /**
     * 获取插入值
     *
     * @param info                统计信息
     * @param list                统计数据列表
     * @param startTimeAndEndTime 开始时间和结束时间
     * @return {@link String}
     */
    public static String getInsertValues(StatisticsInfo info, List<Map<String, Object>> list, long[] startTimeAndEndTime, boolean isFillAllValue) {
        return list.stream().map(map -> StringUtil.format("('{}',{},{},'{}','{}')",
                SEQUENCE.nextId(), getValues(map, info.getGroupBy(), isFillAllValue ? info.getGroupByAndAllValue() : null),
                getValues(map, info.getAggregationFields().keySet(), null),
                startTimeAndEndTime[0], startTimeAndEndTime[1])).collect(Collectors.joining(","));
    }

    /**
     * 获取值
     *
     * @param map     地图
     * @param columns 柱
     * @return {@link String}
     */
    public static String getValues(Map<String, Object> map, Collection<String> columns, Map<String, String> groupByAndAllValue) {
        return columns.stream().map(c -> {
            String fillValue = "NULL";
            if (groupByAndAllValue != null) {
                String value = groupByAndAllValue.get(c);
                fillValue = value != null ? value : fillValue;
            }
            return map.get(c) != null ? "'" + map.get(c) + "'" : fillValue;
        }).collect(Collectors.joining(","));
    }

    /**
     * 生成组合
     *
     * @param groupByList        分组列表
     * @param start              开始
     * @param currentCombination 当前组合
     * @param allCombinations    所有组合
     */
    public static void generateCombinations(List<String> groupByList, int start, List<String> currentCombination, List<List<String>> allCombinations) {
        // 将当前组合添加到结果列表中
        int size = groupByList.size();
        if (size != currentCombination.size()) {
            allCombinations.add(new ArrayList<>(currentCombination));
        }

        // 递归终止条件：当开始位置等于人数时，不再继续生成组合
        if (start == size) {
            return;
        }

        // 递归生成组合
        for (int i = start; i < size; i++) {
            // 添加当前元素到组合中
            currentCombination.add(groupByList.get(i));
            // 递归调用生成剩余元素的组合
            generateCombinations(groupByList, i + 1, currentCombination, allCombinations);
            // 回溯，移除最后一个添加的元素，尝试下一个元素
            currentCombination.remove(currentCombination.size() - 1);
        }
    }

}
